package com.yuzhitong.shapi.util.download;

import com.yuzhitong.shapi.net.APIService;
import com.yuzhitong.shapi.util.LoggerUtil;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.TimeUnit;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.internal.operators.observable.ObservableInternalHelper;
import io.reactivex.rxjava3.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;

public class DownloadUtils {

    private static final String TAG = "DownloadUtils";

    private static final int DEFAULT_TIMEOUT = 15;

    private final Retrofit retrofit;

    private final DownloadListener listener;

    private final CompositeDisposable compositeDisposable;

    private long lenAll = 0;
    private long available;


    public DownloadUtils(String baseUrl, DownloadListener listener) {

        this.listener = listener;
        compositeDisposable = new CompositeDisposable();
        DownloadInterceptor mInterceptor = new DownloadInterceptor(listener);

        OkHttpClient httpClient = new OkHttpClient.Builder()
                .addInterceptor(mInterceptor)
                .retryOnConnectionFailure(true)
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .build();

        retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(httpClient)
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .build();
    }

    /**
     * 开始下载
     *
     * @param url      下载地址
     * @param filePath 文件保存路径
     */
    public void download(@NonNull String url, final String filePath) {
        if (listener != null) {
            listener.onStartDownload();
        }


        try {
            URLConnection openConnection = new URL(url).openConnection();
            available = openConnection.getContentLength();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // subscribeOn()改变调用它之前代码的线程
        // observeOn()改变调用它之后代码的线程
        retrofit.create(APIService.class)
                .download(url)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .map(new Function<ResponseBody, InputStream>() {

                    @Override
                    public InputStream apply(ResponseBody responseBody) throws Throwable {
                        return responseBody.byteStream();
                    }
                })
                .observeOn(Schedulers.computation()) // 用于计算任务
                .doOnNext(ObservableInternalHelper.observerOnNext(new Observer<InputStream>() {
                            @Override
                            public void onSubscribe(@NonNull Disposable d) {
                                compositeDisposable.add(d);
                            }

                            @Override
                            public void onNext(@NonNull InputStream inputStream) {
                                writeFile(inputStream, filePath);
                            }

                            @Override
                            public void onError(@NonNull Throwable e) {
                                LoggerUtil.e("doOnNext----onError");
                                listener.onFail("doOnNext");
                            }

                            @Override
                            public void onComplete() {
                                LoggerUtil.e("doOnNext----onComplete");
                            }
                        })
                )
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<InputStream>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {

                    }

                    @Override
                    public void onNext(@NonNull InputStream inputStream) {
                        LoggerUtil.e("subscribe----onNext");

                        if (lenAll < available){
                            listener.onFail("downloadError");
                            return;
                        }
                        listener.onFinishDownload();
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        LoggerUtil.e("subscribe----onError");
                        listener.onFail("subscribe");
                    }

                    @Override
                    public void onComplete() {
                    }
                });

    }

    /**
     * 将输入流写入文件
     *
     * @param inputString
     * @param filePath
     */
    private void writeFile(InputStream inputString, String filePath) {

        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }
        lenAll = 0;

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);

            byte[] b = new byte[1024];

            int len;
            while ((len = inputString.read(b)) != -1) {
                lenAll += len;
                fos.write(b, 0, len);
            }
            if (lenAll < available){
                listener.onFail("downloadError");
            }

            inputString.close();
            fos.close();

        } catch (FileNotFoundException e) {
            file.delete();
            listener.onFail("FileNotFoundException:");
        } catch (IOException e) {
            file.delete();
            listener.onFail("IOException");
        }

    }

    public void setCompositeDisposable() {
        //暂停下载
        if (!compositeDisposable.isDisposed()) {
            if (compositeDisposable.size() != 0) {
                compositeDisposable.clear();
            }
        }
    }
}
